package l2ft.gameserver.model.instances;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import javolution.text.TextBuilder;
import javolution.util.FastMap;
import l2ft.gameserver.Config;
import l2ft.gameserver.ThreadPoolManager;
import l2ft.gameserver.database.DatabaseFactory;
import l2ft.gameserver.model.Creature;
import l2ft.gameserver.model.Player;
import l2ft.gameserver.model.Skill;
import l2ft.gameserver.model.Summon;
import l2ft.gameserver.model.entity.events.fightclubmanager.FightClubEventManager;
import l2ft.gameserver.network.l2.s2c.MagicSkillUse;
import l2ft.gameserver.network.l2.s2c.NpcHtmlMessage;
import l2ft.gameserver.network.l2.s2c.TutorialShowHtml;
import l2ft.gameserver.tables.SkillTable;
import l2ft.gameserver.templates.npc.NpcTemplate;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BufferInstance extends NpcInstance
{
	private static final Logger _log = LoggerFactory.getLogger(BufferInstance.class);
	
	private static BufferInstance _instance;
	private static Map<Integer,NpcInstance> _nearlyChamberlains = new FastMap<>();
	private static final BuffIndexComparator _buffIndexComparator = new BuffIndexComparator();
	private static final int _buffsPerPage = 6;
	private static double[] pages;
	private List<int[][]> _listOfBuffs = new ArrayList<>();
	private List<String[]> _listOfDesc = new ArrayList<>();
	private static final String[] _allowedLetters = {"q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "a", "s", "d", "f", "g", "h", "j", "k", "l", "z", "x", "c", "v", "b", "n", "m", "1", "2", "3", "6", "5", "4", "7", "8", "9"," "};
	private static final int[][] _buffs = {
			{1499, 1},//Improved Combat
			{1501, 1}, //Improved Condition
			{1503, 1}, //Improved Shield Defense
			{1504, 1}, //Improved Movement
			{1500, 1}, //Improved Magic
			{1502, 1}, //Improved Critical Attack
			{1040, 3}, //Shield
			{1059, 3}, //Empower
			{1068, 3}, //Might
			{1086, 2}, //Haste
			{1077, 3}, //Focus
			{1204, 2}, //Wind Walk
			{1242, 3}, //Death Whisper
			{1232, 3}, //Blazing Skin
			{1492, 1}, //Flame Armor
			{1494, 1}, //Hurricane Armor
			{1493, 1}, //Frost Armor
			{1307, 3}, //Prayer
			{1036, 2}, //Magic Barrier
			{1045, 6}, //Bless the Body
			{1048, 6}, //Bless the Soul
			{1243, 6}, //Bless Shield
			{1087, 3}, //Agility
			{1085, 3}, //Acumen
			{1062, 2}, //Berserker Spirit
			{1392, 3}, //Resist Holy
			{1393, 3}, //Resist Dark
			{1354, 1}, //Arcane Protection
			{1353, 1}, //Divine Protection
			{1352, 1}, //Elemental Protection
			{1303, 2}, //Wild Magic
			{1268, 4}, //Vampiric Rage
			{1240, 3}, //Guidance
			{1078, 6}, //Concentration
			{1035, 4}, //Mental Shield
			{1460, 1}, //Mana Gain
			{1397, 3}, //Clarity
			{1032, 3}, //Invigor
			{1259, 4}, //Resist Shock
			{1043, 1}, //Holy Weapon
			{1044, 3}, //Regeneration
			{1189, 3}, //Resist Wind
			{1033, 3}, //Resist Poison
			{1548, 3}, //Resist Earth
			{1191, 3}, //Resist Fire
			{1182, 3}, //Resist Aqua
			{1357, 1}, //Prophecy of Wind
			{1355, 1}, //Prophecy of Water
			{1356, 1}, //Prophecy of Fire
			{1542, 1}, //Counter Critical
			{1389, 3}, //Greater Shield
			{1388, 3}, //Greater Might
			{825, 1}, //Sharp Edge
			{826, 1}, //Spike
			{827, 1}, //Restring
			{828, 1}, //Case Harden
			{829, 1}, //Hard Tanning
			{830, 1}, //Embroider
			{4700, 13}, //Gift of Queen
			{4699, 13}, //Blessing of Queen
			{4703, 13}, //Gift of Seraphim
			{4702, 13} //Blessing of Seraphim
			};
	private static final String[] _buffsDescriptions =
	{
			"Increases P. Atk. by 15% and P. Def. by 15%. ",
			"Increases Max HP by 35% and Max MP by 35%. ",
			"Increases Shield Defense by 30% and Shield Defense by 50%. ",
			"Increases Speed by 33 and Evasion by 4. ",
			"Increases M. Atk. by 75% and M. Def. by 30%. ",
			"Increases Critical Rate by 30% and Critical Damage by 35%. ",
			"Increases P. Def. by 15%.",
			"Increases M. Atk. by 75%.",
			"Increases P. Atk. by 15%.",
			"Increases Atk. Spd. by 33%.",
			"Increases Critical Rate by 30%.",
			"Increases Speed by 33.",
			"Increases Critical Damage by 35%.",
			"Transfers 20% of the target's received standard short-range damage back to the enemy.",
			"Increases Resistance to fire attacks by 60 and causes burn damage on the attacking enemy.",
			"Increases Resistance to wind attacks by 60 and slows down the attacking enemy's Atk. Spd.",
			"Increases Resistance to water attacks by 60 and slows down the attacking enemy.",
			"Increases the power of HP recovery magic received by all party members by 12%.",
			"Increases M. Def. by 30%.",
			"Increases Max HP by 35%.",
			"Increases Max MP by 35%.",
			"Increases Shield Defense by 30%.",
			"Increases Evasion by 4.",
			"Increases Casting Spd. by 30%.",
			"Decreases a party member's P. Def. by 8%, M. Def. by 16% and Evasion by 4, and increases P. Atk. by 8%, M. Atk. by 16%, Atk. Spd. by 8%, Casting Spd. by 8%, and Speed by 8. ",
			"Increases Resistance to Holy by 30.",
			"Increases Resistance to Dark by 30.",
			"Increases Resistance to buff-canceling attacks by 30 and Resistance to de-buff attacks by 20.",
			"Increases Resistance to buff-canceling attacks by 30 and Resistance to de-buff attacks by 20.",
			"Increases Resistance to Fire, Water, Wind and Earth attacks. ",
			"Increases by 2 the damage rate of magic. ",
			"9% of the standard melee damage inflicted on the enemy is recovered as HP. ",
			"Increases Accuracy by 4.",
			"Decreases magic cancel damage by 53.",
			"Increases Resistance to Hold/Sleep/Mental attaks by 50.",
			"Increases the recharge power received by the target by 85. ",
			"Decreases physical skill MP consumption by 20%, magic skill MP consumption by 10%, and song/dance skill MP consumption by 20%. Consumes 3 Spirit Ore. ",
			"Increases Resistance to Bleed attacks by 50%.",
			"Increases Resistance to Stun attacks by 40%.",
			"Increases a party member's holy P. Atk. by 20.",
			"Increases HP Regeneration by 20%.",
			"Increases Resistance to Wind by 20.",
			"Increases Resistance to Poison by 50%.",
			"Increases Resistance to Earth attacks by 20.",
			"Increases Resistance to Fire by 20.",
			"Increases Resistance to Water by 20.",
			"A powerful spirit acts to increase a party member's Max HP by 20%, Critical Rate by 20%, magic damage by 20%, P. Atk. by 10%, P. Def. by 20%, Atk. Spd. by 20%, M. Atk. by 20%, M. Def. by 20%, Casting Spd. by 20%, and Resistance to de-buff by 10%. Decreases Speed by 20%. Bestows the ability to recover as HP 5% of the standard melee damage inflicted on the enemy. ",
			"A powerful spirit acts to increase the damage caused by the targeted party member's magic damage by 2, MP Recovery Bonus by 20%, P. Atk. by 10%, P. Def. by 20%, Atk. Spd. by 20%, M. Atk. by 20%, M. Def. by 20%, Casting Spd. by 20%, and Resistance to de-buffs by 10%. Decreases Speed by 20% and MP consumption for skill use by 5%.",
			"A powerful spirit acts to increase a party member's Max MP by 20%, HP Recovery Bonus by 20%, magic damage by 2, Critical Damages by 20%, P. Atk. by 10%, P. Def. by 20%, Atk. Spd. by 20%, M. Atk. by 20%, M. Def. by 20%, Casting Spd. by 20%, and Resistance to de-buffs by 10%. Decreases Speed by 20%.",
			"Increases the target's P. Def. against Critical by 30%. When the target receives an attack above a certain amount of damage, Critical Damage of General Short-Range P. Atk. is increased for 8 seconds. ",
			"Increases P. Def. by 15%.",
			"Increases P. Atk. by 10%.",
			"Sharpens a bladed weapon to increase P. Atk. by 5% and Critical Rate by 20%.",
			"Adds a spike to a blunt weapon to increase P. Atk. by 5% and its weight for shock attacks by 8%",
			"Enhances the string of a bow or crossbow to increase P. Atk. by 5% and range by 100",
			"Enhances the armor surface to increase P. Def. by 10%",
			"Tans armor to increase P. Def. by 5% and Evasion by 2. Works only on light armor users",
			"Embroiders a robe to increase P. Def. by 5% and MP Recovery Bonus by 2.",
			"Queen's buff magic temporarily increases party members' P. Atk. and Accuracy. Effect 3.",
			"Queen's buff magic Temporarily increases your party member's Critical Damage amount rate and power",
			"Unicorn Seraphim's buff magic temporarily reduces party members' magic skill recovery time. Effect 3.",
			"Buff magic used by the Unicorn Seraphim. Party members' MP regeneration bonus temporarily increased. Effect 3.",
			
	};
	private static final int[][] _chants = {
			{1284, 3}, //Chant of Revenge
			{1309, 3}, //Chant of Eagle
			{1362, 1}, //Chant of Spirit
			{1363, 1}, //Chant of Victory
			{1413, 1}, //Magnus' chant
			{1390, 3}, //War Chant
			{1391, 3}, //Earth Chant
			{1461, 1}, //Chant of Protection
			{1517, 1}, //Chant of Combat
			{1518, 1}, //Chant of Critical Attack
			{1519, 1}, //Chant of Blood Awakening
			{1535, 1}, //Chant of Movement
			{1549, 1}, //Chant of Elements
			{1562, 1}, //Chant of Berserker
			{1364, 1}, //Eye of Pa'agrio
			};
	private static final String[] _chantsDescriptions =
	{
			"Gives a party member the ability to transfer 20% of received standard short-range damage back to the enemy. ",
			"Increases Accuracy of all party members by 4. ",
			"Increases all party members' Resistance to buff-canceling attacks by 30 and Resistance to de-buff attacks by 20. ",
			"Recovers all party members' HP by 20%, and for 5 minutes, receives help from a great spirit to increase Max HP by 20%, magic damage Critical Damage by 2, Critical Damage by 20%, P. Atk. by 10%, P. Def. by 20%, Atk. Spd. by 20%, M. Atk. by 20%, M. Def. by 20%, Casting Spd. by 20%, Resistance to de-buffs by 10%, and Accuracy by 4. Decreases Speed by 20%.",
			"A powerful spirit acts to increase Max MP of all party members by 15%, MP Recovery Bonus by 1.5 when equipped with light or heavy armor, MP Recovery Bonus by 4 when equipped with a robe, M. Def. by 30%, M. Atk. by 30%, Casting Spd. by 20%, Resistance to Fire, Water, Wind and Earth damage by 10, Resistance to de-buff attacks by 25, and Resistance to buff-canceling attacks by 40.",
			"Increases P. Atk. of all party members by 10%.",
			"Increases P. Def. of all party members by 15%.",
			"Decreases Critical Damage received by a party member by 30%.",
			"Combines party members' P. Atk. increase and P. Def. increase to have more advanced combat power increase effect. Increases P. Atk. by 15% and P. Def. by 15%. ",
			"Combines party members' Critical Rate increase and Critical Damage increase to have more advanced critical increase effect. Increases Critical Rate by 30% and Critical Damage by 35%.",
			"Combines party members' general attack damage absorption and Atk. Spd. increase to have a more advanced blood awakening effect. Increases Atk. Spd. by 33% and bestows the ability to recover as HP 9% of the standard melee damage inflicted on the enemy. ",
			"Combines a party member's Speed increase and Evasion increase effects for a more advanced movement increase effect. Increases Speed by 33 and Evasion by 4.",
			"Increases the elemental attribute of all party members by 20. ",
			"Decreases all party members' P. Def. by 5%, M. Def. by 10% and Evasion by 2, and increases P. Atk. by 5%, M. Atk. by 10%, Atk. Spd. by 5%, Casting Spd. by 5%, and Speed by 5.",
			"Increases nearby clan members' accuracy by 4 and decreases the rate of being hit by a critical attack by 30%."
	};
	private static final int[][] _songs = {
			{264, 1}, //Song of Earth
			{265, 1}, //Song of Life
			{266, 1}, //Song of Water
			{267, 1}, //Song of Warding
			{268, 1}, //Song of Wind
			{269, 1}, //Song of Hunter
			{270, 1}, //Song of Invocation
			{304, 1}, //Song of Vitality
			{305, 1}, //Song of Vengeance
			{306, 1}, //Song of Flame Guard
			{308, 1}, //Song of Storm Guard
			{349, 1}, //Song of Renewal
			{363, 1}, //Song of Meditation
			{364, 1}, //Song of Champion
			{529, 1}, //Song of Elemental
			{764, 1}, //Song of Wind Storm
			{914, 1}, //Song of Purification
			};
	private static final String[] _songsDescriptions =
	{
			"Increases P. Def. of all party members by 25%.",
			"Increases HP Regeneration of all party members by 20%.",
			"Increases Evasion of all party members by 3.",
			"Increases M. Def. of all party members by 30%s.",
			"Increases Speed of all party members by 20.",
			"Increases Speed by 7.",
			"Increases all party members' Resistance to Dark by 20.",
			"Increases Max HP of all party members by 30%.",
			"Gives a party member the ability to transfer 20% of received standard short-range damage back to the enemy.",
			"Increases party members' Resistance to Fire by 30.",
			"Increases party members' Resistance to Wind by 30.",
			"Decreases all party members' physical/magic skill MP consumption by 5% and re-use time by 20%.",
			"Increases all party members' MP Recovery Bonus by 20%, and decreases magic skill use MP consumption by 10%.",
			"Decreases all party members' MP consumption by 20% and reuse time by 10% for physical/sing/dance skill use.",
			"Increases all party members' Resistance to Fire, Water, Wind and Earth attacks by 30.",
			"Increases a party member's Resistance to bows by 10 and Resistance to crossbows by 5.",
			"Provides an 80% chance of removing a party member's de-buffs. Increases Resistance to de-buff attacks by 30% and increases the power of received HP recovery magic by 30%. ",
	};
	private static final int[][] _dances = {
			{271, 1}, //Dance of Warrior
			{272, 1}, //Dance of Inspiration
			{273, 1}, //Dance of Mystic
			{274, 1}, //Dance of Fire
			{275, 1}, //Dance of Fury
			{276, 1}, //Dance of Concentration
			{277, 1}, //Dance of Light
			{307, 1}, //Dance of Aqua Guard
			{309, 1}, //Dance of Earth Guard
			{310, 1}, //Dance of the Vampire
			{311, 1}, //Dance of Protection
			{365, 1}, //Dance of Siren
			{530, 1}, //Dance of Alignment
			{915, 1}, //Dance of Berserker
			};
	private static final String[] _dancesDescriptions =
	{
			"Increases P. Atk. of all party members by 12%.",
			"Increases Accuracy of all party members by 4.",
			"Increases M. Atk. of all party members by 20%.",
			"Increases Critical Damage of all party members by 35%.",
			"Increases Atk. Spd. of all party members by 15%.",
			"Decreases all party members' magic cancel damage by 40 and increases Casting Spd. by 30%.",
			"Increases holy P. Atk. of all party members by 20.",
			"Increases party members' Resistance to Water by 30.",
			"Increases party members' Resistance to Earth by 30.",
			"Gives all party members the ability to recover as HP 8% of any standard melee damage inflicted on the enemy.",
			"Gives all party members the ability to decrease by 30 any environment-related damage received.",
			"Increases all party members' magic damage by 100%.",
			"Increases all party members' Resistance to Holy or Dark attacks by 30.",
			"Decreases a party member's P. Def., M. Def. and Evasion, and increases their P. Atk., M. Atk., Atk. Spd., Casting Spd. and Speed. ",
	};
	
	public BufferInstance(int objectId, NpcTemplate template) 
	{
		super(objectId, template);
		_listOfBuffs.add(_buffs);
		_listOfBuffs.add(_chants);
		_listOfBuffs.add(_songs);
		_listOfBuffs.add(_dances);
		_listOfDesc.add(_buffsDescriptions);
		_listOfDesc.add(_chantsDescriptions);
		_listOfDesc.add(_songsDescriptions);
		_listOfDesc.add(_dancesDescriptions);
		pages = new double[_listOfBuffs.size()];
		for(int i = 0;i<_listOfBuffs.size();i++)
		{
			pages[i] = Math.floor(_listOfBuffs.get(i).length/6);
			if(pages[i]<_listOfBuffs.get(i).length/6.0)
				pages[i]++;
		}
		if(_instance == null)
			_instance = this;
	}
	
	@Override
	public void onBypassFeedback(final Player player, String command) 
	{
		if(!canBypassCheck(player, this))
			return;

		System.out.println("comand:"+command+" testPassed:"+testPassed(player));
		if(!testPassed(player))
			return;
		
		StringTokenizer st = new StringTokenizer(command);
		String mainCommand = st.nextToken();
		int buffPlayer = (st.hasMoreTokens() ? Integer.parseInt(st.nextToken()) : 1);//Player - 1, Pet - 2
		if(buffPlayer != 1 && player.getPet() == null)
			buffPlayer = 1;
		if(mainCommand.equals("mainPage"))
		{
			showMainWindow(player, buffPlayer);
		}
		else if(mainCommand.equals("buffs"))
		{
			int index = Integer.parseInt(st.nextToken());
			int page = Integer.parseInt(st.nextToken());
			showBuffPage(player, buffPlayer, index, page, 0);
		}
		else if(mainCommand.equals("buff"))
		{
			int listId = Integer.parseInt(st.nextToken());
			int page = Integer.parseInt(st.nextToken());
			int index = Integer.parseInt(st.nextToken());
			
			//Fight Club
			if(player.isInFightClub())
			{
				if(!player.getFightClubEvent().canUseBuffer(player, false))
				{
					player.sendMessage("Cannot buff right now!");
					showBuffPage(player, buffPlayer, listId, page, 0);
					return;
				}
				else if(!player.getFightClubEvent().canUseBuffer(player, true))
				{
					double currentHp = player.getCurrentHp();
					double currentCp = player.getCurrentCp();
					double currentMp = player.getCurrentMp();
					
					buff(player, buffPlayer, listId, index);
					
					player.setCurrentHpMp(currentHp, currentMp);
					player.setCurrentCp(currentCp);
					
					showBuffPage(player, buffPlayer, listId, page, 0);
					return;
				}
			}
			
			buff(player, buffPlayer, listId, index);
			showBuffPage(player, buffPlayer, listId, page, 0);
		}
		else if(mainCommand.equals("desc"))
		{
			int listId = Integer.parseInt(st.nextToken());
			int page = Integer.parseInt(st.nextToken());
			int index = Integer.parseInt(st.nextToken());
			showBuffPage(player, buffPlayer, listId, page, (index+1));
		}
		else if(mainCommand.equals("heal"))
		{
			if(player.isInFightClub() && !player.getFightClubEvent().canUseBuffer(player, true))
			{
				player.sendMessage("Cannot heal right now!");
				showMainWindow(player, buffPlayer);
				return;
			}
			broadcastPacket(new MagicSkillUse(this, player, 1258, 1, 0, 0));
			player.setCurrentHpMp(player.getMaxHp(), player.getMaxMp(), true);
			player.setCurrentCp(player.getMaxCp());
			if(player.getPet() != null && !player.getPet().isDead())
			{
				Summon pet = player.getPet();
				pet.setCurrentHpMp(pet.getMaxHp(), pet.getMaxMp());
			}
			showMainWindow(player, buffPlayer);
		}
		else if(mainCommand.equals("remove"))
		{
			broadcastPacket(new MagicSkillUse(this, player, 1056, 1, 0, 0));
			if(buffPlayer == 1)
				player.getEffectList().stopAllEffects();
			else
				player.getPet().getEffectList().stopAllEffects();
			showMainWindow(player, buffPlayer);
		}
		else if(mainCommand.equals("schemeBuff"))
		{
			if(player.isInFightClub() && !player.getFightClubEvent().canUseBuffer(player, false))
			{
				player.sendMessage("Cannot buff right now!");
				showMainWindow(player, buffPlayer);
				return;
			}
			
			boolean canHeal = !player.isInFightClub() || player.getFightClubEvent().canUseBuffer(player, true);

			//Fight Club
			double currentHp = player.getCurrentHp();
			double currentCp = player.getCurrentCp();
			double currentMp = player.getCurrentMp();
			
			if(player.getMainScheme().size()>0)
			{
				if(!st.hasMoreTokens())
				{
					final Scheme scheme = player.getMainScheme();
					buffScheme(player, buffPlayer, scheme, canHeal, true);
				}
				else
				{
					
					int index = Integer.parseInt(st.nextToken());
					Scheme scheme = player.getAllSchemes().get(index);
					scheme.setMainScheme(true);
					buffScheme(player, buffPlayer, scheme, canHeal, true);
				}
			}
			else
				showSimpleMessage(player, buffPlayer, "You don't have a scheme yet!", true, true);
			
			//Fight Club
			if(player.isInFightClub() && !player.getFightClubEvent().canUseBuffer(player, true))
			{
				player.setCurrentHpMp(currentHp, currentMp);
				player.setCurrentCp(currentCp);
			}
		}
		else if(mainCommand.equals("schemeMain"))
		{
			showSchemeMain(player, buffPlayer);
		}
		else if(mainCommand.equals("newScheme"))
		{
			if(player.getAllSchemes().size()>=3)
			{
				showSimpleMessage(player, buffPlayer, "You can do maximally 3 Schemes!");
				return;
			}
			String name = "";
			if(st.hasMoreTokens())
				name = st.nextToken();
			String checkMessage = nameCheck(player, name);
			if(checkMessage.isEmpty()){
				//making new Scheme
				Scheme scheme = new Scheme(player, name, true);
				//setting it as main
				scheme.setMainScheme(true);
				//setting old scheme as NOT main
				for(Scheme oldScheme : player.getAllSchemes())
					if(oldScheme.isMainScheme())
						oldScheme.setMainScheme(false);
				player.getAllSchemes().add(scheme);
				//showing new page
				showAddBuffScheme(player, buffPlayer, scheme, 0);
			}
			else
				showSimpleMessage(player, buffPlayer, checkMessage);
		}
		else if(mainCommand.equals("schemeAddPage"))
		{
			int index = Integer.parseInt(st.nextToken());
			//getting Scheme by index
			Scheme scheme = player.getAllSchemes().get(index);
			//if guy cant add anything else then showing remove page
			if(scheme.getAllSlotsLeft() == 0)
				showRemovePage(player, buffPlayer, scheme);
			else//if not then choosen page
			{
				int page = Integer.parseInt(st.nextToken());
				showAddBuffScheme(player, buffPlayer, scheme, page);
			}
		}
		else if(mainCommand.equals("removePage"))
		{
			int index = Integer.parseInt(st.nextToken());
			Scheme scheme = player.getAllSchemes().get(index);
			showRemovePage(player, buffPlayer, scheme);
		}
		else if(mainCommand.equals("addToScheme"))
		{
			int index = Integer.parseInt(st.nextToken());
			int page = Integer.parseInt(st.nextToken());
			int id = Integer.parseInt(st.nextToken());
			int level = Integer.parseInt(st.nextToken());
			//getting scheme by index
			Scheme scheme = player.getAllSchemes().get(index);
			//getting buff by skillId and level
			Buff buff = new Buff(SkillTable.getInstance().getInfo(id, level));
			//adding to scheme
			scheme.addNewBuff(buff);
			//setting new index
			buff.setIndex(scheme.getNextIndex(buff));
			//showing add buff page
			showAddBuffScheme(player, buffPlayer, scheme, page);
		}
		else if(mainCommand.equals("setMainScheme"))
		{
			int index = Integer.parseInt(st.nextToken());
			Scheme scheme = player.getAllSchemes().get(index);
			//removing other Main Scheme
			for(Scheme oldScheme : player.getAllSchemes())
				if(oldScheme.isMainScheme())
					oldScheme.setMainScheme(false);
			//setting new scheme as main
			scheme.setMainScheme(true);
			//showing main scheme page
			showSchemeMain(player, buffPlayer);
		}
		else if(mainCommand.equals("removeBuff"))
		{
			int index = Integer.parseInt(st.nextToken());
			int buffId = Integer.parseInt(st.nextToken());
			//getting sheme
			Scheme scheme = player.getAllSchemes().get(index);
			//removing buff from scheme
			for(Buff singleBuff : scheme)
				if(singleBuff.getSkill().getId() == buffId)
				{
					scheme.removeBuff(singleBuff);
					break;
				}
			
			showRemovePage(player, buffPlayer, scheme);
		}
		else if(mainCommand.equals("deleteScheme"))
		{
			int index = Integer.parseInt(st.nextToken());
			//getting scheme by id
			Scheme scheme = player.getAllSchemes().get(index);
			//is it main Scheme
			boolean isMainScheme = scheme.isMainScheme();
			//removing scheme from list
			player.getAllSchemes().remove(scheme);
			//if its main scheme, then setting first taken scheme as main
			if(isMainScheme)
				if(player.getAllSchemes().size()>0)
					player.getAllSchemes().get(0).setMainScheme(true);
			//showing main page of schemes
			showSchemeMain(player, buffPlayer);
		}
	}

	private void buffScheme(final Player player, final int buffPlayer, final Scheme scheme, final boolean heal, final boolean sendMainPage) 
	{
		final List<Creature> targets = new ArrayList<>();
		if(buffPlayer == 1)
			targets.add(player);
		else
			targets.add(player.getPet());
		ThreadPoolManager.getInstance().executeTask(new Runnable()
		{
			@Override
			public void run() 
			{
				System.out.println("buff scheme");
				for(Buff buff : scheme)
				{
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					Skill skill = buff.getSkill();
					if(player.isInFightClub())
						skill.setPreservedOnDeath(true);
					callSkill(skill, targets, true);
					skill.setPreservedOnDeath(false);
					System.out.println("calling skill:"+buff.getSkill().getName());
				}
				System.out.println("buffed");
				
				if(heal)
				{
					player.setCurrentHpMp(player.getMaxHp(), player.getMaxMp(), true);
					player.setCurrentCp(player.getMaxCp());
					
					if(player.getPet() != null && !player.getPet().isDead())
					{
						Summon pet = player.getPet();
						pet.setCurrentHpMp(pet.getMaxHp(), pet.getMaxMp());
					}
				}
				if(sendMainPage)
					showMainWindow(player, buffPlayer);
			}});
	}

	private void showBuffPage(Player player, int buffPlayer, int listId, int page, int description)
	{
		int[][] list = _listOfBuffs.get(listId);
		String text = "</center><table>";
		String name = "";
		String icon = "";
		String desc = "";
		if(description>0){
			description--;
			desc = _listOfDesc.get(listId)[(description)];
		}
		int max = page*_buffsPerPage+_buffsPerPage;
		if(max >list.length)
			max = list.length;
		for(int i = page*_buffsPerPage;i<max;i++)
		{
			name = SkillTable.getInstance().getInfo(list[i][0], list[i][1]).getName();
			icon = SkillTable.getInstance().getInfo(list[i][0], list[i][1]).getIcon();
			text += "<tr><td>";
			text += "<button value=\" \" action=\"bypass -h npc_%objectId%_buff "+buffPlayer+" "+listId+" "+page+" "+i+"\" width=32 height=32 back=\""+icon+"\" fore=\""+icon+"\">";
			text += "</td><td width=150><font color=\"LEVEL\">"+name+"</font>";
			if(!desc.isEmpty() && i == description)
				text += "<br>"+desc+"</td></tr>";
			else
				text += "<button value=\"Description\" action=\"bypass -h npc_%objectId%_desc "+buffPlayer+" "+listId+" "+page+" "+i+"\" width=100 height=20 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\"></td></tr>";
		}
		text += "</table><center><table><tr>";
		for(int i = 0;i<pages[listId];i++)
			if(page != i)
				text += "<td><button value=\""+(i+1)+"\" action=\"bypass -h npc_%objectId%_buffs "+buffPlayer+" "+listId+" "+i+"\" width=20 height=25 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\"></td>";
			else
				text += "<td>"+(page+1)+"</td>";
		text += "</tr></table>";
		text += "<button value=\"Back\" action=\"bypass -h npc_%objectId%_mainPage "+buffPlayer+"\" width=160 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\">";
		showSimpleMessage(player, buffPlayer, text);
	}
	
	private void buff(Player player, int buffPlayer, int listId, int index)
	{
		int[][] list = _listOfBuffs.get(listId);
		broadcastPacket(new MagicSkillUse(this, player, list[index][0], list[index][1], 0, 0));
		List<Creature> targets = new ArrayList<>();
		if(buffPlayer == 1)
			targets.add(player);
		else
			targets.add(player.getPet());
		
		Skill skill = SkillTable.getInstance().getInfo(list[index][0], list[index][1]);
		if(player.isInFightClub())
			skill.setPreservedOnDeath(true);
		callSkill(skill, targets, true);
		skill.setPreservedOnDeath(false);
	}
	
	private void buff(NpcInstance fakePc, int skillId, int skillLevel)
	{
		List<Creature> targets = new ArrayList<>();
		targets.add(fakePc);
		Skill skill = SkillTable.getInstance().getInfo(skillId, skillLevel);
		callSkill(skill, targets, true);
	}
	
	@Override
	public void showChatWindow(Player player, int val, Object... replace) 
	{
		if(testPassed(player))
			showFirstPage(player);
	}
	
	private boolean testPassed(Player player)
	{
		if(player.isGM())
			return true;
		if(player.getKarma()>0 && !player.isCursedWeaponEquipped())
			showSimpleMessage(player, 1, "You have too much Karma!<br>Come Back, when you will get over it!");
		else if(player.getPvpFlag()>0)
			showSimpleMessage(player, 1, "I am sorry, i can not buff you while you have PvP Flag!");
		else if(player.isInCombat())
			showSimpleMessage(player, 1, "I am sorry, i can not buff you right now!");
		else
			return true;
		return false;
	}
	
	private void showMainWindow(Player player, int buffingPlayer)
	{
		String text = "";
		if(player.getPet() != null)
		{
			text += "<table><tr><td>";
			if(buffingPlayer == 1)
				text += "<button value=\"Buff Pet\" action=\"bypass -h npc_%objectId%_mainPage 2\" width=102 height=25 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\">";
			else
				text += "<button value=\"Buff yourself\" action=\"bypass -h npc_%objectId%_mainPage 1\" width=102 height=25 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\">";
			text += "</td></tr></table>";
		}
		text += "<table><tr>";
		text += "<td width=100><center><img src=\"icon.skill1085\" width=32 height=32></td>";
		text += "<td width=100><center><img src=\"icon.skill1362\" width=32 height=32></td>";
		text += "</tr><tr>";
		text += "<td><center><button value=\"Buffs\" action=\"bypass -h npc_%objectId%_buffs "+buffingPlayer+" 0 0\" width=62 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\"></td>";
		text += "<td><center><button value=\"Chants\" action=\"bypass -h npc_%objectId%_buffs "+buffingPlayer+" 1 0\" width=62 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\"></td>";
		text += "</tr><tr>";
		text += "<td><center><img src=\"icon.skill0268\" width=32 height=32></td>";
		text += "<td><center><img src=\"icon.skill0271\" width=32 height=32></td>";
		text += "</tr><tr>";
		text += "<td><center><button value=\"Songs\" action=\"bypass -h npc_%objectId%_buffs "+buffingPlayer+" 2 0\" width=62 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\"></td>";
		text += "<td><center><button value=\"Dances\" action=\"bypass -h npc_%objectId%_buffs "+buffingPlayer+" 3 0\" width=62 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\"><br></td>";
		text += "</tr><tr>";
		text += "<td><center><img src=\"icon.skill1258\" width=32 height=32></td>";
		text += "<td><center><img src=\"icon.skill1056\" width=32 height=32></td>";
		text += "</tr><tr>";
		text += "<td><center><button value=\"Heal\" action=\"bypass -h npc_%objectId%_heal "+buffingPlayer+"\" width=62 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\"></td>";
		text += "<td><center><button value=\"Remove Buffs\" action=\"bypass -h npc_%objectId%_remove "+buffingPlayer+"\" width=102 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\"></td>";
		text += "</tr><tr>";
		text += "<td><center><img src=\"icon.skill0435\" width=32 height=32></td>";
		if(player.getMainScheme() != null && player.getMainScheme().size()>0)
			text += "<td><center><img src=\"icon.skill0433\" width=32 height=32></td>";
		text += "</tr><tr>";
		text += "<td><center><button value=\"Scheme Info\" action=\"bypass -h npc_%objectId%_schemeMain "+buffingPlayer+"\" width=102 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\"></td>";
		if(player.getMainScheme() != null && player.getMainScheme().size()>0)
			text += "<td><center><button value=\"Scheme Buff\" action=\"bypass -h npc_%objectId%_schemeBuff "+buffingPlayer+"\" width=102 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\"></td>";
		text += "</tr></table>";
		showSimpleMessage(player, buffingPlayer, text);
	}
	
	private void showSchemeMain(Player player, int buffPlayer)
	{
		boolean foundMain = false;
		for(Scheme scheme : player.getAllSchemes())
			if(scheme.isMainScheme())
				if(!foundMain)
					foundMain = true;
				else
					scheme.setMainScheme(false);
		String text = "";
		text += "<table>";
		for(int i = 0;i<4;i++){
			text += "<tr>";
			for(Scheme scheme : player.getAllSchemes())
			{
				int index = player.getAllSchemes().indexOf(scheme);
				text += "<td>";
				switch(i){
				case 0:
					text += "<button value=\""+scheme.getName()+"\" action=\"bypass -h npc_%objectId%_schemeBuff "+buffPlayer+" "+index+"\" width=82 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\">";
					break;
				case 1:
					text += "<button value=\"Edit\" action=\"bypass -h npc_%objectId%_schemeAddPage "+buffPlayer+" "+index+" 0\" width=82 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\">";
					break;
				case 2:
					text += "<button value=\"Delete\" action=\"bypass -h npc_%objectId%_deleteScheme "+buffPlayer+" "+index+"\" width=82 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\">";
					break;
				case 3:
					if(!scheme.isMainScheme())
						text += "<button value=\"Set Main\" action=\"bypass -h npc_%objectId%_setMainScheme "+buffPlayer+" "+index+"\" width=82 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\">";
				}
				text += "</td>";
			}
			text += "</tr>";
		}
		text += "</table>";
		text += "Do you wish to create new Scheme?<br>";
		text += "Name: <edit var=\"scheme_name\" width=100 height=12>";
		text += "<button value=\"Create\" action=\"bypass -h npc_%objectId%_newScheme "+buffPlayer+" $scheme_name\" width=102 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\"></td>";
	
		showSimpleMessage(player, buffPlayer, text);
	}

	private void showAddBuffScheme(Player player, int buffPlayer, Scheme scheme, int page)
	{
		int index = player.getAllSchemes().indexOf(scheme);
		//buffs and dances text at the top
		String text = "<table><tr><td>Buffs left: <font color=\"LEVEL\">"+scheme.getBuffsLeft()+"</font> Dances/Songs left: <font color=\"LEVEL\">"+scheme.getMusicLeft()+"</font>  <font color=\"43444c\">.</font></td>";
		//if there are more buffs than 0, showing Remove button
		if(scheme.size() > 0)
			text += "<td><button value=\"Remove\" action=\"bypass -h npc_%objectId%_removePage "+buffPlayer+" "+index+"\" width=62 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\"></td>";
		
		text += "</tr></table><br> Add to Scheme:<br>";
		text += "<table>";
		int buffNumber = 0;
		if(scheme.getBuffsLeft()>0)
			{
				for(int[] buffToChoose : _buffs){
					if(!checkIfSchemeContainsBuff(scheme, buffToChoose[0])){
						if(buffNumber >= page*7 && buffNumber < page*7+7)
							text += addBuffToList(buffToChoose, buffPlayer, index, page);
						buffNumber++;
					}
				}
				for(int[] buffToChoose : _chants){
					if(!checkIfSchemeContainsBuff(scheme, buffToChoose[0])){
						if(buffNumber >= page*7 && buffNumber < page*7+7)
							text += addBuffToList(buffToChoose, buffPlayer, index, page);
						buffNumber++;
					}
				}
		}
		if(scheme.getMusicLeft()>0)
		{
			for(int[] buffToChoose : _dances){
				if(!checkIfSchemeContainsBuff(scheme, buffToChoose[0])){
					if(buffNumber >= page*7 && buffNumber < page*7+7)
						text += addBuffToList(buffToChoose, buffPlayer, index, page);
					buffNumber++;
				}
			}
			for(int[] buffToChoose : _songs){
				if(!checkIfSchemeContainsBuff(scheme, buffToChoose[0])){
					if(buffNumber >= page*7 && buffNumber < page*7+7)
						text += addBuffToList(buffToChoose, buffPlayer, index, page);
					buffNumber++;
				}
			}
		}
		text += "</table><table><tr>";
		double max = buffNumber/7.0;
		if(Math.floor(max)<max)
			max = Math.floor(max)+1;
		for(int i = 0;i<max;i++)
			if(page != i)
				text += "<td><button value=\""+(i+1)+"\" action=\"bypass -h npc_%objectId%_schemeAddPage "+buffPlayer+" "+index+" "+i+"\" width=20 height=25 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\"></td>";
			else
				text += "<td>"+(page+1)+"</td>";
		text += "</tr></table>";
		showSimpleMessage(player, buffPlayer, text, true, false);
	}
	
	private static String addBuffToList(int[] buffToChoose, int buffPlayer, int schemeIndex, int page)
	{
		String name = "";
		String icon = "";
		String text = "";
		name = SkillTable.getInstance().getInfo(buffToChoose[0], buffToChoose[1]).getName();
		icon = SkillTable.getInstance().getInfo(buffToChoose[0], buffToChoose[1]).getIcon();
		text += "<tr><td>";
		text += "<img src=\""+icon+"\" width=32 height=32>";
		text += "</td><td><font color=\"LEVEL\">"+name+"</font>";
		text += "</td><td><button value=\"Add\" action=\"bypass -h npc_%objectId%_addToScheme "+buffPlayer+" "+schemeIndex+" "+page+" "+buffToChoose[0]+" "+buffToChoose[1]+"\" width=32 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\">";
		text += "</td></tr>";
		return text;
	}
	
	private void showRemovePage(Player player, int buffPlayer, Scheme scheme) 
	{
		int index = player.getAllSchemes().indexOf(scheme);
		String text = "";
		if(scheme.getAllSlotsLeft()>0)
			text += "<button value=\"Add Buffs\" action=\"bypass -h npc_%objectId%_schemeAddPage "+buffPlayer+" "+index+" 0\" width=82 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\">";
		
		text += "Click on Buff Icon to remove it from Scheme.</center><br><table><tr>";
		//dances and songs that will be shown later
		List<Skill> dancesAndSongs = new ArrayList<>();
		//number of buffs added to text
		int number = 0;
		for(Buff buff : scheme)
		{
			Skill buffSkill = buff.getSkill();
			//getting picture
			String icon = buffSkill.getIcon();
			//every 8 shown buff icons - new lines
			if(number % 8 == 0 && number > 0){
				text += "</tr><tr>";
			}
			//if buff isnt Dance or song and adding added buff number
			if(!buffSkill.isMusic()){
				text += "<td><button value=\" \" action=\"bypass -h npc_%objectId%_removeBuff "+buffPlayer+" "+index+" "+buffSkill.getId()+"\" width=32 height=32 back=\""+icon+"\" fore=\""+icon+"\"></td>";
				number ++;
			}
			else
			{//if it is, then it adds Skill to dancesAndSongs list
				dancesAndSongs.add(buffSkill);
			}
		}
		//new line
		text += "</tr><tr>";
		//setting new line for dances and songs
		number = 0;
		for(Skill music : dancesAndSongs){
			//getting Icon
			String icon = music.getIcon();
			//every 8 shown buff icons - new lines
			if(number % 8 == 0 && number > 0){
				text += "</tr><tr>";
			}
			text += "<td><button value=\" \" action=\"bypass -h npc_%objectId%_removeBuff "+buffPlayer+" "+index+" "+music.getId()+"\" width=32 height=32 back=\""+icon+"\" fore=\""+icon+"\"></td>";
			number++;
		}
		text += "</tr></table>";
		if(scheme.getAllSlotsLeft() > 0)
			showSimpleMessage(player, buffPlayer, text, true, true);
		else
			showSimpleMessage(player, buffPlayer, text, true, false);
	}
	
	/**
	 * Checking every Buff until finding BuffId.
	 * @return boolean
	 */
	private static boolean checkIfSchemeContainsBuff(Scheme scheme, int buffId)
	{
		for(Buff buff : scheme)
			if(buff.getSkill().getId() == buffId)
				return true;
		return false;
	}
	/**
	 * Checking name length, if it equals to other names and characters inside
	 * @return message or Empty String
	 */
	private static String nameCheck(Player player, String name)
	{
		//if name length less than 2, sends message
		if(name.length()<2)
			return "Scheme name must be at least 2 characters long!";
		//if bigger than 10, sends message
		else if(name.length()>10)
			return "Scheme name can't be longer than 10 characters!";
		//if name exists in any other scheme, send message
		for(Scheme existingScheme : player.getAllSchemes())
			if(name.toLowerCase().equals(existingScheme.getName().toLowerCase()))
				return "You already created scheme with such name!";
		
		String msg = "";
		for(int x = 0;x<name.length();x++){
			msg = "Inappropriate characters were in scheme name, try again!";
			for(int i = 0;i<_allowedLetters.length;i++){
				String letterFromName = name.substring(x, x+1).toLowerCase();
				if(letterFromName.equals(_allowedLetters[i]))
					msg = "";
			}
			if(msg.length()>0)
				return msg;
		}
		return msg;
	}
	/**
	 * Showing first page of the buffer with Button Buff me!
	 */
	private void showFirstPage(Player player)
	{
		String text = "Welcome, "+player.getName()+"<br> I see that you want to empower your soul<br> you will need my services for that!<br>";
		text += "<button value=\"Buff me!\" action=\"bypass -h npc_%objectId%_mainPage 1\" width=62 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\">";
		showSimpleMessage(player, 1, text);
	}
	
	/**
	 * Showing message without Back button and with Image at the top
	 * @param player
	 * @param text
	 */
	private void showSimpleMessage(Player player, int buffPlayer, String text)
	{
		showSimpleMessage(player, buffPlayer, text, false, true);
	}

	/**
	 * Sending requested Message to player
	 * 
	 * @param player
	 * @param text - text inside
	 * @param mainPageButton - Back button
	 * @param showHeroTower - Image at the top
	 */
	private void showSimpleMessage(Player player, int buffPlayer, String text, boolean mainPageButton, boolean showHeroTower)
	{
		TextBuilder tb = new TextBuilder();
		tb.append("<html><title>Npc Buffer</title><body><br><center>");
		tb.append("");
		if(showHeroTower && !player.isInFightClub())
			tb.append("<img src=\"L2UI_CH3.herotower_deco\" width=256 height=32><br>");
		tb.append(text);
		if(mainPageButton)
			tb.append("<br><button value=\"Back\" action=\"bypass -h npc_%objectId%_mainPage "+buffPlayer+"\" width=62 height=32 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_ct1.button_df\">");
		
		if(player.isInFightClub())
		{
			tb.append("<center><br><center><button value = \"Close\" action=\"bypass -h "+FightClubEventManager.BYPASS+" close\" back=\"l2ui_ct1.button.OlympiadWnd_DF_Info_Down\" width=200 height=30 fore=\"l2ui_ct1.button.OlympiadWnd_DF_Info\"></center>");
			player.sendPacket(new TutorialShowHtml(tb.toString()));
		}
		else
		{
			NpcHtmlMessage msg = new NpcHtmlMessage(getObjectId());
			msg.setHtml(tb.toString());
		
			player.sendPacket(msg);
		}
	}
	
	public static class Buff
	{
		private int _index;
		private Skill _skill;
		
		public Buff(Skill skill)
		{
			_skill = skill;
		}
		
		public Buff(Skill skill, int index)
		{
			index = _index;
			_skill = skill;
		}
		
		public Skill getSkill()
		{
			return _skill;
		}
		
		public int getIndex()
		{
			return _index;
		}
		
		public void setIndex(int i)
		{
			_index = i;
		}
		
		public boolean isMusic()
		{
			return _skill.isMusic();
		}
	}
	
	public static class Scheme extends ArrayList<Buff>
	{
		private String _name;
		private boolean _main;
		private int _musicLeft;
		private int _buffsLeft;
		
		public Scheme(Player owner, String name, boolean main)
		{
			_name = name;
			_main = main;
			updateBuffCount(owner);
		}

		public String getName()
		{
			return _name;
		}
		
		public boolean isMainScheme()
		{
			return _main;
		}
		
		public void setMainScheme(boolean b)
		{
			_main = b;
		}
		
		/**
		 * Always first add it to scheme
		 */
		public int getNextIndex(Buff buff) 
		{
			return size()-1;
		}
		
		public void addNewBuff(Buff skill)
		{
			super.add(skill);
			updateSingleBuff(skill, true);
		}
		
		public void removeBuff(Buff skill)
		{
			super.remove(skill);
			updateSingleBuff(skill, false);
			updateIndexes();
		}
		
		public int getMusicLeft()
		{
			return _musicLeft;
		}
		
		public int getBuffsLeft()
		{
			return _buffsLeft;
		}
		
		public int getAllSlotsLeft()
		{
			return _buffsLeft+_musicLeft;
		}
		
		private void updateIndexes()
		{
			int m = 0;
			int b = 0;
			for(Buff buff : this)
			{
				if(buff.isMusic() && buff.getIndex() != m)
					buff.setIndex(m);
				else if(!buff.isMusic() && buff.getIndex() != b)
					buff.setIndex(b);
				m++;
				b++;
			}
		}
		
		private void updateSingleBuff(Buff skill, boolean add)
		{
			if(skill.isMusic())
				if(add)
					_musicLeft--;
				else
					_musicLeft++;
			else
				if(add)
					_buffsLeft--;
				else
					_buffsLeft++;
		}
		/**
		 * Updating _musicLeft and _buffsLeft
		 */
		private void updateBuffCount(Player player)
		{
			_musicLeft = Config.ALT_MUSIC_LIMIT;
			_buffsLeft = player.getBuffLimit();
			for(Buff buff : this)
				if(buff.isMusic())
					_musicLeft--;
				else
					_buffsLeft--;
		}
	}
	
	/**
	 * Comparing Buff Indexes
	 */
	private static class BuffIndexComparator implements Comparator<Buff>
	{
		@Override
		public int compare(Buff o1, Buff o2) {
			return o1.getIndex() - o2.getIndex();
		}
	}
	
	/**
	 * Loading player Schemes
	 */
	public static void loadSchemes(Player player) {
		Map<String, Scheme> schemes = new FastMap<>();
		
		try (Connection con = DatabaseFactory.getConnection();
				PreparedStatement statement = con.prepareStatement("SELECT * FROM character_buff_schemes WHERE char_id="+player.getObjectId());
				ResultSet rset = statement.executeQuery())
		{
			
			while(rset.next())
			{
				//getting name
				String name = rset.getString("scheme_name");
				//getting skill
				Skill skill = SkillTable.getInstance().getInfo(rset.getInt("buff_id"), rset.getInt("buff_level"));
				//changing it to Buff
				Buff buff = new Buff(skill);
				buff.setIndex(rset.getInt("buff_index"));
				
				if(schemes.containsKey(name))
					schemes.get(name).addNewBuff(buff);
				else
				{
					//is it main Scheme
					boolean mainScheme = rset.getInt("main") == 1 ? true : false;
					//setting new Scheme
					Scheme scheme = new Scheme(player, name, mainScheme);
					//adding buff
					scheme.addNewBuff(buff);
					//adding it to schemes
					schemes.put(name, scheme);
				}
			}
		}
		catch(Exception e)
		{
			_log.error("Count not load schemes "+e);
		}
		
		//setting it as List
		List<Scheme> schemeList = new ArrayList<>();
		schemeList.addAll(schemes.values());
		//sorting buffs by Index
		for(Scheme scheme : schemes.values())
			Collections.sort(scheme, _buffIndexComparator);
		//setting schemes
		player.setAllSchemes(schemeList);
	}
	
	/**
	 * Saving Player Schemes to database
	 */
	public static void saveSchemes(Player player)
	{
		try (Connection con = DatabaseFactory.getConnection())
		{
			
			try(PreparedStatement statement = con.prepareStatement("DELETE FROM character_buff_schemes WHERE char_id="+player.getObjectId()))
			{
				//first deleting every buff of player
				statement.execute();
			}
			
			//later insert new buff
			try(PreparedStatement statement = con.prepareStatement("INSERT INTO character_buff_schemes (char_id, scheme_name, buff_id, buff_level, main, buff_index) VALUES (?,?,?,?,?,?)"))
			{
				for(Scheme scheme : player.getAllSchemes())
				{
					for(Buff buff : scheme)
					{
						statement.setInt(1, player.getObjectId());
						statement.setString(2, scheme.getName());
						statement.setInt(3, buff.getSkill().getId());
						statement.setInt(4, buff.getSkill().getLevel());
						statement.setInt(5, scheme.isMainScheme() == true ? 1 : 0);
						statement.setInt(6, buff.getIndex());
						statement.execute();
					}
				}
			}
		}
		catch(Exception e)
		{
			_log.error("could not save Schemes:" + e);
		}
	}
	
	public static void buffFakePc(NpcInstance fakePc, int skillId, int skillLevel)
	{
		_instance.buff(fakePc, skillId, skillLevel);
	}
	
	public static void showFightClubMain(Player player)
	{
		_instance.onBypassFeedback(player, "mainPage");
	}
	
	public static void bypass(Player player, String command)
	{
		System.out.println("Bypass:"+command);
		_instance.onBypassFeedback(player, command.replace("npc_%objectId%_", ""));
	}
	
	/**
	 * It also heals
	 */
	public static void buffDefaultScheme(Player player)
	{
		//If default scheme doesnt exist, it creates one
		addDefaultScheme(player);
		Scheme scheme = player.getMainScheme();
		
		try
		{
			_instance.buffScheme(player, 1, scheme, true, false);
		}
		catch(NullPointerException e)
		{
			_log.error("Buffer isn't spawned!");
		}
	}
	
	/**
	 * Used for Chamberlains
	 */
	public static void showMainPage(Player player, NpcInstance chamberlain)
	{
		_nearlyChamberlains.put(player.getObjectId(), chamberlain);
		player.setTarget(_instance);
		_instance.showChatWindow(player, 0);
	}


	public static boolean canBypassCheck(Player player, NpcInstance npc)
	{
		System.out.println("bypass check");
		if(!player.isInFightClub() && player.isActionsDisabled())
		{
			player.sendActionFailed();
			return false;
		}
		if(!npc.isInRange(player, INTERACTION_DISTANCE))
		{
			if(player.isInFightClub())
			{
				System.out.println("returned true");
				return true;
			}
			System.out.println("returned false");
			if(_nearlyChamberlains.get(player.getObjectId()) != null)
			{
				if(_nearlyChamberlains.get(player.getObjectId()).isInRange(player, INTERACTION_DISTANCE))
					return true;
				_nearlyChamberlains.remove(player.getObjectId());
			}
			return false;
		}
		System.out.println("returned true");
		return true;
	}

	private static final int[][] MAGE_SET = {{349, 1},{364, 1},{914, 1},{1501, 1},{1499, 1},{1503, 1},{1504, 1},{1500, 1},{1085, 3},{1062, 2},{1389, 3},{1363, 1},{1392, 3},{1393, 3},{1353, 1},{1078, 6},{1397, 3},{273, 1},{276, 1},{365, 1},{530, 1},{915, 1},{268, 1},{304, 1},{529, 1},{914, 1},{1364, 1},{1461, 1},{4703, 13},{830, 1}};
	private static final int[][] FIGHTER_SET = {{1499, 1},{1501, 1},{1504, 1},{1502, 1},{1062, 2},{1035, 4},{1542, 1},{1388, 3},{1284, 3},{1363, 1},{1461, 1},{1519, 1},{271, 1},{274, 1},{275, 1},{310, 1},{264, 1},{530, 1},{267, 1},{268, 1},{269, 1},{304, 1},{364, 1},{4699, 13},{1352, 1},{1259, 4},{1364, 1},{1397, 3},{349, 1}};
	/**
	 * Adding default scheme to new @player
	 */
	public static void addDefaultScheme(Player player)
	{
		if(player.getAllSchemes().size() > 0)
			return;
		
		Scheme scheme = new Scheme(player, "Default", true);
		//setting it as main
		scheme.setMainScheme(true);
		//setting old scheme as NOT main
		for(Scheme oldScheme : player.getAllSchemes())
			if(oldScheme.isMainScheme())
				oldScheme.setMainScheme(false);
		
		int[][] set;
		//choosing set
		if(player.isMageClass())
			set = MAGE_SET;
		else
			set = FIGHTER_SET;
		//adding buffs to scheme
		for(int[] skill : set)
			scheme.addNewBuff(new Buff(SkillTable.getInstance().getInfo(skill[0], skill[1])));
		//adding scheme to player
		player.getAllSchemes().add(scheme);
	}
}
